import { createHash } from 'crypto';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { posix, resolve } from 'path';

import Axios from 'axios';
import JoinURL from 'url-join';

import { C, dirCacheBundle, dirCacheManifest } from '../../../lib/global.js';

import { toSize } from './lib/Tool.js';


export async function fetchManifest(urlManifest, version, wog) {
	const idManifest = posix.parse(urlManifest).name;

	const pathManifest = resolve(dirCacheManifest, `${version}-${idManifest}.manifest`);

	wog.add(wog.wait, '下载[索引]', '', {
		1: { stat: wog.ready, title: '大小', text: '' },
		2: { stat: wog.ready, title: '位置', text: '' },
	});

	try {
		let bufferManifest;

		if(existsSync(pathManifest)) {
			bufferManifest = readFileSync(pathManifest);

			wog.set(
				0, wog.done, '本地缓存',
				1, wog.done, toSize(bufferManifest.length),
				2, wog.done, pathManifest,
			);
		}
		else {
			wog.set(1, urlManifest);

			bufferManifest = (await Axios.get(urlManifest, { responseType: 'arraybuffer', proxy: C.proxy, timeout: 1000 * 60 * 4 })).data;

			writeFileSync(pathManifest, bufferManifest);

			wog.set(
				0, wog.done, '完成',
				1, wog.done, toSize(bufferManifest.length),
				2, wog.done, pathManifest,
			);
		}

		return bufferManifest;
	}
	catch(error) {
		wog.set(0, wog.error, error?.message ?? error?.toString() ?? `[${typeof error}] ${error}`);

		throw error;
	}
}

export async function fetchEntry(wog) {
	const urlEntry = JoinURL(C.gameServer.sie, `/api/v1/products/lol/version-sets/${C.gameServer.region}?q[artifact_type_id]=lol-game-client&q[platform]=windows`);

	wog.add(wog.wait, '下载[目录]', urlEntry, {
		1: { stat: wog.ready, title: '最新版本', text: '' },
	});

	try {
		const { data } = await Axios.get(urlEntry, { proxy: C.gameServer.proxy, timeout: 1000 * 60 * 4 });

		const content = C.gameServer.region == 'PBE1' ? 'beta' : 'release';

		const releases = data.releases.filter(release => release.release.labels.content.values[0] == content);

		const versionLatest = C.gameServer.version || Math.max(...releases.map(release => ~~release.release.labels['riot:revision'].values[0]));

		wog.set(
			0, wog.done, '完成',
			1, wog.done, versionLatest,
		);

		const urlsManifest = data.releases
			.filter(release => release.release.labels['riot:revision'].values[0] == versionLatest)
			.map(release => release.download.url);

		if(urlsManifest.length != 1) {
			throw Error(`已终止。Manifest数量(${urlsManifest.length})不为1`);
		}

		return [urlsManifest[0], versionLatest];
	}
	catch(error) {
		wog.set(0, wog.error, error?.message ?? error?.toString() ?? `[${typeof error}] ${error}`);

		throw error;
	}
}

export async function fetchBundle(id, cdn, counter, wog) {
	const idBundle = ('0000000000000000' + id.toString(16)).slice(-16).toUpperCase();

	const pathBundle = resolve(dirCacheBundle, `${idBundle}.bundle`);

	let bufferBundle;
	if(existsSync(pathBundle)) {
		wog.set(1, wog.done, `(${++counter.now}/${counter.max})[${idBundle}] 本地缓存`);

		bufferBundle = readFileSync(pathBundle);
	}
	else {
		const slot = `(${counter.now + 1}/${counter.max})[${idBundle}]`;
		wog.set(1, wog.wait, `${slot} 下载中...`);

		const bundleURL = JoinURL(cdn, `channels/public/bundles/${idBundle}.bundle`);

		let timesFetched = 0;
		let passFetched = false;

		while(timesFetched++ <= 4) {
			try {
				const { data, headers } = await Axios.get(bundleURL, { responseType: 'arraybuffer', proxy: C.server.proxy, timeout: 1000 * 60 * 4 });

				if(data.length != headers['content-length']) {
					throw Error(`${slot} 下载错误，数据长度(${data.length})与响应头长度(${headers['content-length']})不一致。第${timesFetched}次重试`);
				}
				else {
					const hash = createHash('md5');
					hash.update(data);

					const etag = `"${hash.digest('hex')}"`;
					if(headers.etag.toLowerCase() != etag.toLowerCase()) {
						throw Error(`${slot} 下载错误，数据MD5(${etag})与响应头ETAG(${headers.etag.toLowerCase()})不一致。第${timesFetched}次重试`);
					}
					else {
						passFetched = true;
					}
				}

				if(passFetched) {
					bufferBundle = data;

					writeFileSync(pathBundle, bufferBundle);

					wog.set(1, wog.done, `(${++counter.now}/${counter.max})[${idBundle}] 下载成功`);

					break;
				}
			}
			catch(error) {
				wog.set(1, wog.error, error?.message ?? error?.toString() ?? `[${typeof error}] ${error}`);
			}
		}

		if(!passFetched) {
			throw Error(`${slot} 下载失败，超过重试次数`);
		}
	}

	return [idBundle, bufferBundle];
}